package pl.edu.pw.elka.dzielins.revanalyzer.model;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import pl.edu.pw.elka.dzielins.revanalyzer.instance.InstanceObject;
import pl.edu.pw.elka.dzielins.revanalyzer.utils.CombinatoricsUtils;

import edu.mit.csail.sdg.alloy4compiler.ast.Module;

public class Model {
	private Set<ModelObject> objects;
	private Set<ModelFact> facts;
	private Module module;
	private String modulePath;

	public Model() {
		this.objects = new HashSet<ModelObject>();
		this.facts = new HashSet<ModelFact>();
	}

	// getters & setters

	public Set<ModelObject> getObjects() {
		return objects;
	}

	public void setObjects(Set<ModelObject> objects) {
		this.objects = objects;
	}
	
	public Set<ModelFact> getFacts() {
		return facts;
	}

	public void setFacts(Set<ModelFact> facts) {
		this.facts = facts;
	}	

	public Module getModule() {
		return module;
	}

	public void setModule(Module module) {
		this.module = module;
	}
	
	public String getModulePath() {
		return modulePath;
	}

	public void setModulePath(String modulePath) {
		this.modulePath = modulePath;
	}
	
	// other methods

	public ModelObject getObject(String name) {
		for (ModelObject object : objects) {
			if (object.getName().equals(name))
				return object;
		}
		return null;
	}
	
	public ModelFact getFact(String name) {
		for (ModelFact fact : facts) {
			if (fact.getName().equals(name))
				return fact;
		}
		return null;
	}
	
	public Map<ModelObject,Set<ModelObject>> getHiearchyMap() {
		Map<ModelObject, Set<ModelObject>> map = new HashMap<ModelObject, Set<ModelObject>>();
		Set<ModelObject> set;
		for (ModelObject object : objects) {
			if (!map.containsKey(object))
				map.put(object, new HashSet<ModelObject>());
			if (!map.containsKey(object.getBaseObject()))
				map.put(object.getBaseObject(), new HashSet<ModelObject>());
			set = map.get(object.getBaseObject());
			set.add(object);
		}
		return map;
	}
	
	/**
	 * Generates partial less restrictive models. Set of partial model's restrictions is a subset of  base model's restrictions.
	 * @return Set of partial models derived from this model.
	 */
	public Set<Model> getPartialModels() {
		Set<Model> results = new HashSet<Model>();
		Set<Set<ModelFact>> combinations;
		// generate all possible subsets
		combinations = CombinatoricsUtils.getAllCombinations(getFacts());
		for(Set<ModelFact> combination:combinations) {
			Model model = new Model();
			// same objects as base model
			model.setObjects(getObjects());
			// same module as base model
			model.setModule(getModule());
			model.setModulePath(getModulePath());
			// subset of facts
			model.setFacts(combination);
			results.add(model);
		}
		return results;
	}
	
	public Set<Model> getPartialModels(int k) {
		Set<Model> results = new HashSet<Model>();
		Set<Set<ModelFact>> combinations;
		// generate all possible subsets
		combinations = CombinatoricsUtils.getCombinations(getFacts(), k);
		for(Set<ModelFact> combination:combinations) {
			Model model = new Model();
			// same objects as base model
			model.setObjects(getObjects());
			// same module as base model
			model.setModule(getModule());
			model.setModulePath(getModulePath());
			// subset of facts
			model.setFacts(combination);
			results.add(model);
		}
		return results;
	}
	
	public Set<ModelFact> getMissingFacts(Set<ModelFact> presentFacts) {
		Set<ModelFact> result = new HashSet<ModelFact>();
		for (ModelFact fact : facts) {
			if (!presentFacts.contains(fact))
				result.add(fact);
		}
		return result;
	}

	@Override
	public String toString() {
		String s="";
		for(ModelObject object : objects)
			s+=object.toString()+"\n";
		//for(ModelFact fact : facts)
			//s+=fact.toString()+"\n";
		return s;
	}
}
